﻿#ifndef PST_GRAPH3DWINDOWBASE_H
#define PST_GRAPH3DWINDOWBASE_H

#include "FieldGraphGenerator.h"
#include <vtkSmartPointer.h>
#include <vtkCaptionActor2D.h>
#include <vtkActor.h>
#include <opencv2/opencv.hpp>
#include <QHash>
#include <QList>
#include <QVector>
#include <QWidget>
#include <vtkDistanceToCamera.h>
#include <Tuple>
#include <QDomNodeList>
#include <QDomElement>
#include <QDomText>
#include <QDomAttr>
#include "BaseModule/GlobalHelper.h"

//class vtkActor;
class vtkProp;
class vtkCamera;
class vtkPointSet;
class vtkAxesActor;
class vtkRenderer;
class vtkCubeAxesActor;
class vtkCallbackCommand;
class vtkRenderWindow;
class QVTKOpenGLNativeWidget;
class vtkRenderWindowInteractor;
class vtkOrientationMarkerWidget;
class vtkGenericOpenGLRenderWindow;
class vtkWindowToImageFilter;
class vtkSphereSource;
class QListWidgetItem;
//class vtkCaptionActor2D;

namespace pst
{
    class SurfaceElectricFieldMapSidebarSettings;
    class FieldGraphGenerator;
    class InfomationGraph3DBasical;
    class PropPickerCallbackHander;
    class AnimationSettingData;
    class PostMarkerInteractionStyle;
    class FramelessDragableTableWidget;
    class AnimationTooBarInfoData;
    class FieldScalarBarGenerator;
    class GeomtryActorGenerator;

    class Marker3DDataInfo :public QObject
    {
        Q_OBJECT

    public:
        explicit Marker3DDataInfo(QObject* parent = nullptr) : QObject(parent) {}

    public:
        int m_markerId = 0;//给marker分配的id
        int m_pickedId = 0;//在vtkActor中的id
        QString m_markerName = "";
        double m_markerPosition[3] = { 0, 0, 0 };
        int m_markerType = 0;
        QString m_fieldName = "";
        int m_fieldIndex = -1;
        vtkActor* m_sourceActor = nullptr; //从那个actor创建的marker
        int m_graphInfoID = -1;
        vtkSmartPointer<vtkActor> m_markerActor = nullptr;
        QString m_markerValue = 0;
        vtkSmartPointer<vtkCaptionActor2D> m_markerTextActor = nullptr;
        vtkSmartPointer<vtkDistanceToCamera> m_distanceToCamera = nullptr;
        //三维云图特殊数据
        int m_showType{ 0 };
        int m_fixedParamIndex{ 0 };

        int m_markerSize = 30;
        int m_markerTextFontSize = 10;

        bool operator==(const Marker3DDataInfo& other) const
        {
            return this->m_pickedId == other.m_pickedId
                && this->m_markerName == other.m_markerName
                && qFuzzyCompare(this->m_markerPosition[0], other.m_markerPosition[0])
                && qFuzzyCompare(this->m_markerPosition[1], other.m_markerPosition[1])
                && qFuzzyCompare(this->m_markerPosition[2], other.m_markerPosition[2])
                && this->m_markerType == other.m_markerType
                && this->m_fieldIndex == other.m_fieldIndex
                && this->m_fieldName == other.m_fieldName 
                && this->m_sourceActor == other.m_sourceActor 
                && this->m_graphInfoID == other.m_graphInfoID
                && this->m_markerActor == other.m_markerActor
                && this->m_markerValue == other.m_markerValue
                && this->m_markerTextActor == other.m_markerTextActor
                && this->m_showType == other.m_showType
                && this->m_fixedParamIndex == other.m_fixedParamIndex
                && this->m_markerSize == other.m_markerSize
                && this->m_markerTextFontSize == other.m_markerTextFontSize
                && this->m_distanceToCamera == other.m_distanceToCamera;
        }

        QDomElement& writeToProjectFile(QDomDocument* doc, QDomElement* element, bool isdiso = false)
        {
            QDomElement _Marker3DDataInfoRoot = doc->createElement("Marker3DDataInfo");

            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "MarkerId", QString::number(m_markerId));
            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "PpickedId", QString::number(m_pickedId));
            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "MarkerName", m_markerName);
            auto _markerPosition= QString::number(m_markerPosition[0]) + "," + QString::number(m_markerPosition[1]) + "," + QString::number(m_markerPosition[2]);
            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "MarkerPosition", _markerPosition);
            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "MarkerType", QString::number(m_markerType));
            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "GraphInfoID", QString::number(m_graphInfoID));
            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "FieldName", m_fieldName);
            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "MarkerValue", m_markerValue);
            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "ShowType", QString::number(m_showType));
            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "FixedParamIndex", QString::number(m_fixedParamIndex));
            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "MarkerSize", QString::number(m_markerSize));
            GlobalHelperInstance->createDomElement(doc, &_Marker3DDataInfoRoot, "MarkerTextFontSize", QString::number(m_markerTextFontSize));

            element->appendChild(_Marker3DDataInfoRoot);

            return _Marker3DDataInfoRoot;
        }

        void readFromProjectFile(QDomElement* element, bool isdiso = false)
        {
            auto _MarkerIdNodeList = element->elementsByTagName("MarkerId");
            m_markerId = _MarkerIdNodeList.item(0).toElement().text().toInt();
            auto _PpickedIdNodeList = element->elementsByTagName("PpickedId");
            m_pickedId = _PpickedIdNodeList.item(0).toElement().text().toInt();
            auto _MarkerNameNodeList = element->elementsByTagName("MarkerName");
            m_markerName = _MarkerNameNodeList.item(0).toElement().text();
            auto _MarkerPositionNodeList = element->elementsByTagName("MarkerPosition");
            auto _markerPositionList = _MarkerPositionNodeList.item(0).toElement().text().split(",");
            m_markerPosition[0] = _markerPositionList[0].toDouble();
            m_markerPosition[1] = _markerPositionList[1].toDouble();
            m_markerPosition[2] = _markerPositionList[2].toDouble();
            auto _MarkerTypeNodeList = element->elementsByTagName("MarkerType");
            m_markerType = _MarkerTypeNodeList.item(0).toElement().text().toInt();
            auto _GraphInfoIDNodeList = element->elementsByTagName("GraphInfoID");
            m_graphInfoID = _GraphInfoIDNodeList.item(0).toElement().text().toInt();
            auto _FieldNameNodeList = element->elementsByTagName("FieldName");
            m_fieldName = _FieldNameNodeList.item(0).toElement().text();
            auto _MarkerValueNodeList = element->elementsByTagName("MarkerValue");
            m_markerValue = _MarkerValueNodeList.item(0).toElement().text();
            auto _ShowTypeNodeList = element->elementsByTagName("ShowType");
            m_showType = _ShowTypeNodeList.item(0).toElement().text().toInt();
            auto _FixedParamIndexNodeList = element->elementsByTagName("FixedParamIndex");
            m_fixedParamIndex = _FixedParamIndexNodeList.item(0).toElement().text().toInt();
            auto _MarkerSizeNodeList = element->elementsByTagName("MarkerSize");
            m_markerSize = _MarkerSizeNodeList.item(0).toElement().text().toInt();
            auto _MarkerTextFontSizeNodeList = element->elementsByTagName("MarkerTextFontSize");
            m_markerTextFontSize = _MarkerTextFontSizeNodeList.item(0).toElement().text().toInt();
        }
    };

    class Graph3DWindowBase : public QWidget
    {
        Q_OBJECT

    public:
        explicit Graph3DWindowBase(QWidget* parent = 0);
        virtual ~Graph3DWindowBase();

        void AddFixedPixelAxes(vtkRenderer* renderer, vtkRenderWindow* renderWindow);

        void updatePickedDataDisplayWindowVisibility();
        virtual void addAMarkerDataToPickedDataDisplayWindow(Marker3DDataInfo* info);
        void removeAMarkerDataFromPickedDataDisplayWindow(int markerId);
        void clearPickedDataDisplayWindow();

        void pyRemoveAllMarker();
        void pyExportTableData(const QString& fileName);

        virtual void setWindowID(int windowID) = 0;
        virtual int getWindowID() const = 0;

        virtual void setWindowUniversalID(int windowID) = 0;
        virtual int getWindowUniversalID() const = 0;

        /// @brief 文件写出至工程文件
        virtual QDomElement& writeToProjectFile(QDomDocument* doc, QDomElement* element, bool isdiso = false) = 0;

        /// @brief 从工程文件读入数据
        virtual void readFromProjectFile(QDomElement* e, bool isdiso = false) = 0;     

        virtual void addGraph3D(InfomationGraph3DBasical* info);
        virtual bool saveScreenShot(const QString& path);

        virtual void setResetCamera();
        virtual void setCameraToPositiveX();
        virtual void setCameraToPositiveY();
        virtual void setCameraToPositiveZ();
        virtual void setCameraToNegativeX();
        virtual void setCameraToNegativeY();
        virtual void setCameraToNegativeZ();
        virtual void setCameraToIsometric();
        virtual void zoomCamera(double scale = 1.1);

        virtual void setInteractorStlyleToTranslation();
        virtual void setInteractorStlyleToRotation();
        virtual void setInteractorStlyleToDefault();
        virtual void setInteractorStlyleToPicking();

        virtual void updateGeometryActors(bool isShow, bool& hasAnyGeometrySuccess);
        vtkSmartPointer<vtkActor> importGeomtryActor(const QString& path, bool resetCamera = true);

        void setAnimationType(int type);
        int getAnimationType() const;

        void increaseAnimationSpeed();
        void decreaseAnimationSpeed();
        double getSpeedFactor() const;
        // void setAnimationDataList(QList<vtkPointSet*> dataList);
        // QList<vtkPointSet*> getAnimationDataList() const;

        void setAnimationSettingData(AnimationSettingData* data);
        AnimationSettingData* getAnimationSettingData() const;
        // void setAnimationSpeed(double speed);
        // double getAnimationSpeed() const;

        virtual void setActiveAnimation(bool active);

        // 导出动画 type:0为gif，1为avi
        // void exportAnimationData(const QString& path, int type);
        virtual void exportAnimationData(const QString& path /* int type*/);

        virtual void updateControlPanel();

        //type： 0:point 1:cell
        void addMarkerDataInfo(int infomation3DBasicalId, int type, int fieldIndex, const QString& fieldName, int pickedId,
            const QString& value, vtkActor* pikerActor);

        virtual void updateMarkerValue();//更新marker的值，比如重新选择了场值，动图更新数据

        virtual void exportSourceDataToLocal();

        virtual void pyExportSourceDataToLocal(QString filePath);

        virtual void setActiveClipper(bool active);
        bool getActiveClipper() const;

        virtual void setClipNormalDirection(double x, double y, double z);
        double* getClipNormalDirection();

        virtual void setClipOrigin(double x, double y, double z);
        double* getClipOrigin();

        virtual void setClipInsideOut(bool insideOut);
        bool getClipInsideOut() const;

        virtual void updateClipperAndGeometry(bool activeClipper, bool activeShowGeomtry, bool& hasAnyGeometrySuccess);

        virtual void updateClipper(bool activeClipper);

        bool CreateExpandedPlaneActor(const QList<vtkActor*>& actors,
            const double planeNormal[3], const double origin[3]);

        bool CreateExpandedPlaneActor(const QList<vtkDataSet*>& dataList,
            const double planeNormal[3], const double origin[3]);

        void setIsShowGeometryActors(bool isShow) { m_isShowingGeometry = isShow; }
        bool getIsShowGeometryActors() const { return m_isShowingGeometry; }

        //三维窗口展示动画信息的文本actor
        void initAnimationInfoTextActor();
        vtkSmartPointer<vtkTextActor> getAnimationInfoTextActor();
        void setAnimationInfoText(const QStringList& text);
        QStringList getAnimationInfoText() const;

        //单个info是否包含多个数据，是否可以用于动图
        virtual bool isInfoContainsMultipleData(const InfomationGraph3DBasical* info);
        //判断多个info是否是同一来源，是否可以用于动图
        virtual bool isInfoFromSameSource(QList< InfomationGraph3DBasical*> infoList);
        //根据数据判断是否可以启用动图
        virtual bool whetherCanEnableDataAnimation();
        virtual void updateAnimationButtonState();
        virtual void setFrameToBeforeExportAnimation();
        virtual void sentToolBarAnimationDataInfo(const InfomationGraph3DBasical* info);

        void setAnimationTooBarInfoData(AnimationTooBarInfoData* info);
        AnimationTooBarInfoData* getAnimationTooBarInfoData();

        // 获取 列 结果数据类型， 如果fileResultType==7，需要根据isGainScalar来进行特殊处理
        QString GetResultDataTypeAsString(int result_type, bool isGainScalar =false) const;
        // 获取用于颜色条的类型
        int getScalarBarType(int fileResultType) const;
        //// 获取 列 结果数据类型， 如果fileResultType==7，需要根据isGainScalar来进行特殊处理
        
        virtual QString generateGraphNameAuto(InfomationGraph3DBasical* info) const;

        virtual void updateDBAndUnitData();

        virtual void updateCubeAxesActor(bool isShow = true);
  
        InfomationGraph3DBasical* getInfoFromSidebarNameItem(QListWidgetItem* nameItem);
        QVTKOpenGLNativeWidget* getQvtkWidget()
        {
            return m_qvtkWidget;
        };

        //获取 infolist
        QList<InfomationGraph3DBasical*> getInfoList();

        void updateModelDisplayAndClipperButtonCheckState();

        protected:
            bool isContainGeometryPath(const QList<std::tuple <QStringList, QObject*, GeomtryActorGenerator*>>& geoTuple, 
                const QStringList& path, std::tuple <QStringList, QObject*, GeomtryActorGenerator*>& firstFoundData) const;
            
            bool isGeometryPathAllSame(const QList<InfomationGraph3DBasical*>& infos);

            virtual void updateMarkerVisibility();
            void writeToProjectFile3DWindowBase(QDomDocument* doc, QDomElement* rootElement);
            void readFromProjectFile3DWindowBase(QDomElement* element);
            int getGraphInfoIDByVtkActor(vtkActor* actor);
            void BuildCompleteMarker3DDataInfo(Marker3DDataInfo* MarkerInfo);

            virtual void updateDataActorPickable();//更新数据是否可拾取

    private:
        void AddCornerAxes();
        void initConnections();
        void ResetCameraViewDirection(vtkCamera* camera, double look_x,
            double look_y, double look_z, double up_x, double up_y, double up_z);
        void exportAnimationToLocal();

        virtual void animationToNextDataFrame();

        std::tuple<vtkSmartPointer<vtkActor>, vtkSmartPointer<vtkDistanceToCamera>,
            vtkSmartPointer<vtkSphereSource>, vtkSmartPointer<vtkGlyph3D>>
            generatePointMarkerActor(double x, double y, double z);
        vtkSmartPointer<vtkActor> generateCellMarkerActor(vtkDataSet* data, int pickedId, double& x, double& y, double& z);
        vtkSmartPointer<vtkCaptionActor2D> generateMarkerTextActor(double x, double y, double z, const QString& text);
        QString getColResultTypeAsStringInner(int fileResultType, bool isGainScalar = false) const;


        //InfomationGraph3DBasical* getInfoFromSidebarSetting(SurfaceElectricFieldMapSidebarData* sidebarData);

    public slots:
        virtual void slot_scalarBarDoubleClicked(vtkProp* pickedProp);
        virtual void slot_beginToPlayDataAnimation();
        virtual void slot_beginToPlayCameraAnimation();

        // virtual void slot_addContextMenu(QWidget* parent, QVTKOpenGLNativeWidget* vtkWidget, Graph3DWindowBase* graph3DWindowBase);
        virtual void slot_beginAddPointMarker();
        virtual void slot_beginAddCellMarker();
        virtual void slot_stopAddMarker();
        virtual void slot_removeChosenMarker();
        virtual void slot_removeAllMarker();
        virtual void slot_exportTableData();

        void removeChosenMarkers(const QList<int>& markerIds);
        virtual void slot_addCustomContextMenu(const QPoint& pos);

        void slot_exitAddMarkerState();

    protected:
        QVTKOpenGLNativeWidget* m_qvtkWidget;
        vtkSmartPointer<vtkRenderer> m_render3D;
        vtkSmartPointer<vtkRenderWindow> m_renderWindow3D;
        vtkSmartPointer<vtkRenderWindowInteractor> m_interactor;
        vtkSmartPointer<vtkAxesActor> m_leftDownCornerAxes;
        vtkSmartPointer<vtkOrientationMarkerWidget> m_leftDownCornerWidget;
        vtkSmartPointer<vtkCamera> m_camera;

        vtkSmartPointer<PostMarkerInteractionStyle> m_pickerInteractorStyle;

        //========动画相关设置========//
        int m_animationType;     // 动画类型 0:数据变化 1:相机变化
        double m_animationSpeed; // 动画速度
        AnimationSettingData* m_animationSettingData;
        AnimationTooBarInfoData* m_animationTooBarInfoData;

        QList<InfomationGraph3DBasical*> m_graphInfoList;

        QTimer* m_dataTimer;
        QTimer* m_cameraTimer;
        // QTimer* m_exportAnimationTimer;
        bool m_isExportingAnimation;
        int m_loopCount;
        vtkSmartPointer<vtkWindowToImageFilter> m_windowToImageFilter;
        QList<cv::Mat> m_animationImageList;
        int m_imageSize{ 0 }; // 所有图像的最大帧数
        QString m_exportAnimationPath;

        int m_markerNum{ 0 };
        QHash<int, Marker3DDataInfo*> m_marker3DDataInfoList;

        // FramelessDragableTableWidget* m_markerDataTable;
        FramelessDragableTableWidget* m_markerDataTable;
        //QList<vtkSmartPointer<vtkActor>> m_addedMakerActor;
        int m_pickedDataCount{ 0 };

        // 裁剪相关参数
        bool m_isActiveClip;
        double m_planeNormalDirection[3];
        double m_planeOrigin[3];
        bool m_planeInsideOut{ false };

        //int graphNum{ 0 };//图像编号 用于侧边栏命名
        vtkSmartPointer<vtkActor> m_clipperPlaneActor;
        bool m_isAddingPointMarker;
        bool m_isAddingCellMarker;

        bool m_isShowingGeometry;

        vtkSmartPointer<vtkTextActor> m_animationInfoTextActor;
        bool m_isFirstTimeDataAnimation;
        bool m_isBeginExportAnimation;//是否是开始导出动图

        double m_animationCameraAngle;//本次启动动图后相机的旋转角度
        int m_animationCameraCount;//本次启动动图后相机的旋转次数
        int m_animationDataCount;//本次启动动图后数据的变化次数
        QHash<int, FieldScalarBarGenerator*> m_scalarBarGeneratorMap; // 存放所有的标量条生成器
        QHash<int, int> m_eachTypeGraphCount; // 存放所有的标量条生成器ID

        vtkSmartPointer<vtkCubeAxesActor> m_cubeAxesActor; // 用于显示坐标轴的立方体轴

        //存放设置曲线列表中的曲线和对应的曲线信息，用于从设置栏曲线中重新找回曲线信息
        //QHash<QListWidgetItem*, InfomationGraph3DBasical*> m_curveMap;
        bool m_isCubeAxesActorVisible;

        QList<std::tuple <QStringList, QObject*, GeomtryActorGenerator*>> m_geometrySettingTupleList;
        QHash<int, QString> getMarkerNameMap();

    private:
        // vtkSmartPointer<vtkActor> m_scalarDataActor;
        // vtkSmartPointer<vtkScalarBarActor> m_scalarBarActor;
        // vtkSmartPointer<vtkActor> m_vectorDataActor;
        // vtkSmartPointer<vtkScalarBarActor> m_vectorBarActor;
        // vtkSmartPointer<vtkGenericOpenGLRenderWindow> m_renderWindow3D;

        // QList<FieldGraphGenerator*> m_fieldGraphGeneratorList;

        // QHash<InfomationGraph3DBasical, FieldGraphGenerator*> m_dataMap;

        // 目前只允许添加一个三维图
        // FieldGraphGenerator* m_currentFieldGraphGenerator;
    };
} // namespace pst

#endif
